﻿using CMS.Controls.Events;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

namespace CMS.Controls
{
    public class ScheduleDisplay : Control
    {
        #region 事件
        /// <summary>
        /// 当选择的点被改变时
        /// </summary>
        public event EventHandler SelectChanged;

        #endregion

        #region 私有字段
        private IAdapter _Adapter;

        #endregion

        #region 公有属性
        public new ContextMenuStrip ContextMenuStrip { get; set; }

        /// <summary>
        /// 当前选择的位置,与此位置相关的元素将会显示为激活
        /// </summary>
        public Point SelectedPoint { get; private set; }

        #endregion

        /// <summary>
        /// 课程表显示适配器接口
        /// </summary>
        public interface IAdapter
        {
            /// <summary>
            /// 当数据发生更新时,将使用此事件来提示数据更新
            /// </summary>
            event EventHandler<ScheduleDisplayEventArgs> DataChanged;

            /// <summary>
            /// 绘制元素
            /// </summary>
            /// <param name="week"></param>
            /// <param name="pw"></param>
            /// <param name="ph"></param>
            /// <param name="selected">当前选择的区域的位置</param>
            /// <param name="g"></param>
            void DrawItem(int week, int pw, int ph, Point selected, Graphics g);
        }

        /// <summary>
        /// 适配器
        /// </summary>
        public IAdapter Adapter
        {
            get => _Adapter; set
            {
                if (_Adapter != null)
                {
                    _Adapter.DataChanged -= Value_DataChanged;
                }
                if (value != null)
                {
                    value.DataChanged += Value_DataChanged;
                }
                _Adapter = value;
            }
        }

        private void Value_DataChanged(object sender, ScheduleDisplayEventArgs e)
        {
            if (e.Week == 0)
            {
                Invalidate();
            }
            else
            {
                Invalidate(_GetRectangleByWeek(e.Week));
                if (SelectedPoint.X == e.Week)
                {
                    SelectChanged?.Invoke(this, EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// 构造器
        /// </summary>
        public ScheduleDisplay()
        {
            DoubleBuffered = true;
            BackColor = Color.White;
        }


        #region 控件交互相关

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _UpdateSelected(e.Location);
            }
            else if (e.Button == MouseButtons.Right)
            {
                _UpdateSelected(e.Location);
                ContextMenuStrip?.Show(this, e.Location);
            }
        }

        #endregion

        #region 绘图相关
        /// <summary>
        /// 当空间大小发生改变时,更新参数
        /// </summary>
        /// <param name="e"></param>
        protected override void OnSizeChanged(EventArgs e)
        {
            if (Height / 15 != 1 || Width / 8 != 1)
            {
                Size = new Size(Width - Width % 8 + 1, Height - Height % 15 + 1);   //强制调整大小,使之永远能签个成整数份
            }

            base.OnSizeChanged(e);
            _FrameRect.Width = Width - 1;
            _FrameRect.Height = Height - 1;
            _PerWidth = Width / 8;
            _PerHeight = Height / 15;
        }

        /// <summary>
        /// 整个控件的外边框的大小
        /// </summary>
        private Rectangle _FrameRect = Rectangle.Empty;

        private int _PerWidth = 1;
        private int _PerHeight = 1;

        /// <summary>
        /// 边框的线的画笔
        /// </summary>
        private readonly Pen LinePen = new Pen(Color.LightGray);

        /// <summary>
        /// 标题的画刷
        /// </summary>
        private readonly Brush TitleBrush = Brushes.Black;

        /// <summary>
        /// 标题的绘制格式
        /// </summary>
        private readonly StringFormat TitleFormat = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };

        /// <summary>
        /// 标题字体
        /// </summary>
        private readonly Font TitleFont = new Font("微软雅黑", 15, GraphicsUnit.Pixel);

        /// <summary>
        /// 背景画刷
        /// </summary>
        private Brush BackBrush = Brushes.White;

        /// <summary>
        /// 星期文本用于绘制
        /// </summary>
        public static readonly string[] Weeks = new string[]
        {
            "星期一",
            "星期二",
            "星期三",
            "星期四",
            "星期五",
            "星期六",
            "星期日"
        };

        /// <summary>
        /// 更新选择点,如果确实发生了更新,则会触发事件,并进行相应的处理
        /// </summary>
        /// <param name="location"></param>
        private void _UpdateSelected(Point location)
        {
            Point pos = new Point(location.X / _PerWidth, location.Y / _PerHeight);
            if (pos.X <= 0 || pos.Y <= 0)
            {
                pos = Point.Empty;
            }

            if (!pos.Equals(SelectedPoint))
            {
                if (SelectedPoint.X == pos.X)
                {
                    SelectedPoint = pos;
                    if (SelectedPoint.X != 0)
                    {
                        Invalidate(_GetRectangleByWeek(SelectedPoint.X));
                    }
                }
                else
                {
                    int oldX = SelectedPoint.X;
                    SelectedPoint = pos;
                    if (oldX != 0)
                    {
                        Invalidate(_GetRectangleByWeek(oldX));
                        Application.DoEvents();
                    }
                    if (pos.X != 0)
                    {
                        Invalidate(_GetRectangleByWeek(pos.X));
                    }
                }
                SelectChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        private Rectangle _GetRectangleByWeek(int week)
        {
            return new Rectangle(week * _PerWidth, _PerHeight, _PerWidth, _PerHeight * 14);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.PageUnit = GraphicsUnit.Pixel;
            if (Adapter != null)
            {
                if (e.ClipRectangle.Size.Equals(Size))
                {
                    PaintSheet(g);
                    for (int i = 1; i <= 7; i++)
                    {
                        Adapter.DrawItem(i, _PerWidth, _PerHeight, SelectedPoint, g);
                    }
                }
                else
                {
                    Rectangle tmpRect = new Rectangle(_PerWidth+2, _PerHeight+2, _PerWidth-4, _PerHeight * 14-4);
                    for (int i = 1; i <= 7; i++)
                    {
                        if (!Rectangle.Intersect(tmpRect, e.ClipRectangle).IsEmpty)
                        {
                            PaintSheetPart(g, i);
                            Adapter.DrawItem(i, _PerWidth, _PerHeight, SelectedPoint, g);
                        }
                        tmpRect.Offset(_PerWidth, 0);
                    }
                }
            }
            else
            {
                 PaintSheet(g);
            }
        }

        protected override void OnBackColorChanged(EventArgs e)
        {
            base.OnBackColorChanged(e);
            BackBrush = new SolidBrush(BackColor);
        }

        #region 绘制组成元素

        #endregion
        /// <summary>
        /// 绘制整个表的基础线
        /// </summary>
        /// <param name="e"></param>
        protected void PaintSheet(Graphics g)
        {
            g.DrawRectangle(LinePen, _FrameRect);

            int tmp = _PerHeight;
            for (int i = 1; i <= 14; i++)
            {
                g.DrawLine(LinePen, 0, tmp, _FrameRect.Width, tmp);
                tmp += _PerHeight;
            }

            tmp = _PerWidth;

            for (int i = 1; i <= 7; i++)
            {
                g.DrawLine(LinePen, tmp, 0, tmp, _FrameRect.Height);
                tmp += _PerWidth;
            }

            for (int i = 1; i <= 7; i++)
            {
                g.DrawString(Weeks[i - 1], TitleFont, TitleBrush, new Rectangle(_PerWidth * i, 0, _PerWidth, _PerHeight), TitleFormat);
            }

            for (int i = 1; i <= 14; i++)
            {
                g.DrawString($"第{i}节", TitleFont, TitleBrush, new Rectangle(0, _PerHeight * i, _PerWidth, _PerHeight), TitleFormat);
            }
        }

        /// <summary>
        /// 绘制表的一部分(当对表只有少量的修改时)
        /// </summary>
        protected void PaintSheetPart(Graphics g, int week)
        {
            int left = week * _PerWidth;
            int right = week * _PerWidth + _PerWidth;
            g.FillRectangle(BackBrush, _GetRectangleByWeek(week));
            g.DrawLine(LinePen, left, _PerHeight, left, _FrameRect.Height);
            g.DrawLine(LinePen, right, _PerHeight, right, _FrameRect.Height);

            for (int i = 1; i <= 14; i++)
            {
                g.DrawLine(LinePen, left, _PerHeight * i, right, _PerHeight * i);
            }

        }

        #endregion

    }
}
